/*===========================================================================*\
|
|  File:        linklist.cpp
|
|  Description: 
|   Class to maintain a doubly linked list.
|       
|-----------------------------------------------------------------------------
|
|  Copyright (C) 1995-1996 Microsoft Corporation.  All Rights Reserved.
|
|  Written by Moss Bay Engineering, Inc. under contract to Microsoft Corporation
|
\*===========================================================================*/
#include "linklist.h"

#define NULL    0L

// -----------------------------------------------------------------
//  CLinkedList constructor - initialize to empty
// -----------------------------------------------------------------
CLinkedList::CLinkedList()
{
    pHead = pTail = pCurPosition = NULL;
}

// -----------------------------------------------------------------
//  CLinkedList destructor - free each node
//  Note: we do not free the app data pointed to by each node.
// -----------------------------------------------------------------
CLinkedList::~CLinkedList()
{
    LPNODE  pCur, pNext;

    pCur = pHead;
    pHead = pTail = pCurPosition = NULL;

    // Go thru list and free each node
    while (pCur != NULL)
    {
        pNext = pCur->pNext;
        delete(pCur);
        pCur = pNext;
    }
}   

// -----------------------------------------------------------------
//  GetFirst - return app data for first entry in list and make it
//  the current node.
// -----------------------------------------------------------------
void  * CLinkedList::GetFirst()
{
    pCurPosition = pHead;
    if (pCurPosition == NULL)
    {
        return(NULL);
    }

    return(pCurPosition->pData);
}

// -----------------------------------------------------------------
//  GetLast - return app data to last entry in list and make it
//  the current node.
// -----------------------------------------------------------------
void  * CLinkedList::GetLast()
{
    pCurPosition = pTail;
    if (pCurPosition == NULL)
    {
        return(NULL);
    }

    return(pCurPosition->pData);
}

// -----------------------------------------------------------------
//  GetNext - return next app data entry in list and make it
//  the current node.
// -----------------------------------------------------------------
void  * CLinkedList::GetNext()
{
    LPNODE  pNext;

    // check for empty list or already at end of list.
    if ((pCurPosition == NULL) || (pCurPosition->pNext == NULL))
    {
        return(NULL);
    }

    pNext = pCurPosition->pNext;
    pCurPosition = pNext;
    return(pNext->pData);
}

// -----------------------------------------------------------------
//  GetFirst - return app data that follows a given entry and make it
//  the current node.
// -----------------------------------------------------------------
void  * CLinkedList::GetNext(void  *pData)
{
    pCurPosition = Find(pData);
    return(GetNext());
}

// -----------------------------------------------------------------
//  GetPrev - return app data for previous entry in list and make it
//  the current node.
// -----------------------------------------------------------------
void  * CLinkedList::GetPrev()
{
    LPNODE  pPrev;

    // check for empty list or already at start
    if ((pCurPosition == NULL) || (pCurPosition->pPrev == NULL))
    {
        return(NULL);
    }

    pPrev = pCurPosition->pPrev;
    pCurPosition = pPrev;
    return(pPrev->pData);
    
}
// -----------------------------------------------------------------
//  GetFirst - return app data that preceeds a given entry and make it
//  the current node.
// -----------------------------------------------------------------
void  * CLinkedList::GetPrev(void  *pData)
{
    pCurPosition = Find(pData);
    return(GetPrev());
}

// -----------------------------------------------------------------
//  Add - create a new node and put it at the start of the list and
//  make it the current node.
// -----------------------------------------------------------------
void CLinkedList::Add(void  *pData)
{
    LPNODE  pNew = new NODE;

    // setup node and prepare it for its role as the new head of the list
    pNew->pData = pData;
    pNew->pNext = pHead;
    pNew->pPrev = NULL;

    // The old head of list (if any) should point to new node)
    if (pHead != NULL)
        pHead->pPrev = pNew;

    // Make new node the head and current position
    pHead = pNew;
    pCurPosition = pNew;

    // Check to see if new node is also the tail (ie. only list entry)
    if (pTail == NULL)
        pTail = pNew;
}

// -----------------------------------------------------------------
//  Append - create a new node and put it at the end of the list.
// -----------------------------------------------------------------
void CLinkedList::Append(void  *pData)
{
    LPNODE  pNew = new NODE;

    // setup node and prepare it for its role as the new tail of the list
    pNew->pData = pData;
    pNew->pPrev = pTail;
    pNew->pNext = NULL;

    // The old tail of list (if any) should point to new node.
    if (pTail != NULL)
        pTail->pNext = pNew;

    // Make new node the tail
    pTail = pNew;

    // Check to see if new node is also the head (ie. only list entry)
    if (pHead == NULL)
    {
        pHead = pNew;
        pCurPosition = pNew;
    }
}

// -----------------------------------------------------------------
//  Find - private method to find the node with the specified app data
//  attached to it.
// -----------------------------------------------------------------
LPNODE CLinkedList::Find(void  *pData)
{
    LPNODE  pCur;

    // go thru list until we reach end or we find the right node.
    for (pCur=pHead; (pCur != NULL) && (pCur->pData != pData); pCur= pCur->pNext);
    return(pCur);
}


// -----------------------------------------------------------------
//  Insert - create a new node and put it in front of the current
//  position node and make it the current position.
// -----------------------------------------------------------------
void CLinkedList::Insert(void  *pData)
{
    LPNODE  pNext, pPrev;
    LPNODE  pNew = new NODE;

    pNew->pData = pData;

    // check to be sure that there is a current node
    if (pCurPosition != NULL)
    {
        
        // get pointers of current position
        pPrev = pCurPosition->pPrev;
        pNext = pCurPosition->pNext;

        // set new nodes pointers for insertion into the list
        pNew->pPrev = pPrev;
        pNew->pNext = pCurPosition;

        // Set the node in front of new node (if any) to point to it
        if (pPrev != NULL)
        {
            pPrev->pNext = pNew;

        // No node in front -> new node is at head
        } else {
            pHead = pNew;
        }

        // make new node the current node
        pCurPosition = pNew;

    // No current node, just Add to front
    } else {
        Add(pData);
    }
}

// -----------------------------------------------------------------
//  Insert - create a new node and put it in front of the specified
//  node and make it the current position.
// -----------------------------------------------------------------
void CLinkedList::Insert(void  *pData, void  *pBefore)
{
    // simply make the specified node current and insert the new
    // node.
    pCurPosition = Find(pBefore);
    Insert(pData);
}

// -----------------------------------------------------------------
//  Remove - remove a specified node from the list.
//  Note: we do not delete the app data attached to the node!
// -----------------------------------------------------------------
void CLinkedList::Remove()
{
    LPNODE  pCur, pNext, pPrev;

    pCur = pCurPosition;

    if (pCur != NULL)
    {

        // save a copy of the links
        pPrev = pCur->pPrev;
        pNext = pCur->pNext;

        // Is there a node ahead of us?
        if (pPrev != NULL)
        {
            // yes -> update it to not point to us.
            pPrev->pNext = pNext;
        } else {
            // no -> update head to not point to us.
            pHead = pNext;
            pCurPosition = pNext;
        }

        // Is there a node behind us?
        if (pNext != NULL)
        {
            // yes -> update it to not point to us.
            pNext->pPrev = pPrev;
            pCurPosition = pNext;
        } else {
            // no -> update tail to not point to us.
            pTail = pPrev;
            pCurPosition = pPrev;
        }

        delete(pCur);
    }

}   

// -----------------------------------------------------------------
//  Remove - remove a specified node from the list.
//  Note: we do not delete the app data attached to the node!
// -----------------------------------------------------------------
void CLinkedList::Remove(void  *pData)
{
    pCurPosition = Find(pData);
    Remove();
}   
// -----------------------------------------------------------------
//  RemoveFirst - remove the first node in the list and return the
//  app data associated with it.
// -----------------------------------------------------------------
void * CLinkedList::RemoveFirst()
{
    LPNODE  pCur, pNext;
    void    *pData = NULL;

    pCur = pHead;

    // is there a node at the head?
    if (pCur != NULL)
    {

        // take first node out of list.
        pNext = pCur->pNext;
        pHead = pNext;
        pCurPosition = pNext;

        // are there any nodes after us?
        if (pNext != NULL)
        {
            // yes -> make it the new head
            pNext->pPrev = NULL;
        } else {
            // no -> the list is now empty
            pTail = NULL;
        }

        // get app data for node and then delete it
        pData = pCur->pData;
        delete(pCur);
    }

    return(pData);
}

// -----------------------------------------------------------------
//  RemoveLast - remove the last node in the list and return the
//  app data associated with it.
// -----------------------------------------------------------------
void * CLinkedList::RemoveLast()
{
    LPNODE  pCur, pPrev;
    void    *pData = NULL;

    pCur = pTail;

    // is there a node at the tail?
    if (pCur != NULL)
    {
        // take last node out of list.
        pPrev = pCur->pPrev;
        pTail = pPrev;

        // are there any nodes ahead of us?
        if (pPrev != NULL)
        {
            // yes -> make it the new tail node
            pPrev->pNext = NULL;
        } else {
            // no -> list is now empty
            pHead = NULL;
            pCurPosition = NULL;
        }

        // get app data for node and then delete it
        pData = pCur->pData;
        delete(pCur);
    }

    return(pData);
}
